package org.vectrics.client.framework;



import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import org.vectrics.SystemException;
import org.vectrics.VectricsException;
import org.vectrics.client.module.Module;
import java.util.Vector;
import java.util.Iterator;
import org.vectrics.client.webclient.*;


/**
 * @author mmoore
 */
public class ClientConfiguration {
	private transient static Logger log = Logger.getLogger(ClientConfiguration.class);
	private String fileName = null; 
	private String ipAddress = null;
	private String port = null;
	private String location = null;
	private String applicationName = "Vectrics Enterprise";
	private CommandDispatcher commandManager = null;
	private Module defaultModule = null;
	private Vector modules = new Vector();
	private ClientApplication application = null;
	private ServiceConnection connection = null;
	
	
	
	public ClientConfiguration(String fileName) {
		super();
		this.fileName = fileName;
		if (fileName == null) {
			throw new SystemException("ClientConfiguration constructor called without a fileName specified");
		}
		
		try {
			FileInputStream file = new FileInputStream(fileName);
			this.initialize(file);
		}
		catch (FileNotFoundException fe) {
			log.error("Configuration file not found: " + fileName, fe);
			throw new SystemException("Configuration file not found: " + fileName, fe);
		}
	}

	public ClientConfiguration(InputStream inputStream) {
		super();
		this.initialize(inputStream);
	}
	
	public void initialize(InputStream inputStream) {
		SAXReader reader = new SAXReader();
		try {
			Document doc = reader.read(inputStream);
			getData(doc);
		} catch (DocumentException de) {
			log.error("Error parsing configuration file: " + fileName, de);
			throw new SystemException("Error parsing configuration file: " + fileName, de);
		}
	}


	private void getData(Document doc) {
		Element root = doc.getRootElement();
		String userName = null;
		String password = null;

		Iterator iter = root.elementIterator("application");
		if (iter.hasNext()) {
			Element serverElement = (Element)iter.next();
			String className = serverElement.attributeValue("class");
			this.applicationName = serverElement.attributeValue("name");
			try {
				Class appClass = Class.forName(className);
				try {
					application = (ClientApplication)appClass.newInstance();
				}
				catch (Exception ex) {
					log.error("Exception instantiating class: " + className);
					throw new VectricsException("Exception instantiating application class read from configuration file: " + className, ex);
				}
			}
			catch (Exception ex) {
				log.error("Exception getting class for class name: " + className);
				throw new SystemException("Exception getting class for class name: " + className, ex);
			}
			
		}
		
		
		iter = root.elementIterator("server");
		if (iter.hasNext()) {
			Element serverElement = (Element)iter.next();
			setIpAddress(serverElement.attributeValue("ip"));
			setPort(serverElement.attributeValue("port"));
			setLocation(serverElement.attributeValue("location"));
			
			Iterator userIter = serverElement.elementIterator();
			if (userIter.hasNext()) {
				Element userElement = (Element)userIter.next();
				userName = userElement.attributeValue("name");
				password = userElement.attributeValue("password");
			}
		}
		//commandManager = new CommandManager();
		connection = new ServiceConnection();
		ServerConfig serverConfig = new ServerConfig();
		serverConfig.setIpAddress(this.getIpAddress());
		serverConfig.setPort(this.getPort());
		serverConfig.setLocation(this.getLocation());
		connection.setConfig(serverConfig);
		connection.setUserName(userName);
		connection.setPassword(password);
		
		
		
		iter = root.elementIterator("modules");
		if (iter.hasNext()) {
			Element modulesElement = (Element)iter.next();
			loadModules(modulesElement);
		}
		if (defaultModule == null) {
			log.debug("Choosing first module in list as default module");
			Iterator moduleIter = modules.iterator();
			if (moduleIter.hasNext()) {
				defaultModule = (Module)moduleIter.next();
			}
		}
	}
	
	private void loadModules(Element modulesElement) {
		log.info("Loading client modules...");
		modules.clear();
		Iterator iter = modulesElement.elements().iterator(); //modulesElement.elementIterator("module");
		while (iter.hasNext()) {
			Element moduleElement = (Element)iter.next();
			if (moduleElement.getName().equals("module")) {
				loadModule(moduleElement);
			}
		}
			
	}
	
	private void loadModule(Element moduleElement) {
		String moduleClassName = moduleElement.attributeValue("class");
		//String moduleName = moduleElement.attributeValue("name");
		String defaultModule = moduleElement.attributeValue("default");
		
		log.info("Loading module class: " + moduleClassName);
		try {
			Class moduleClass = Class.forName(moduleClassName);
			try {
				Module module = (Module)moduleClass.newInstance();
				modules.add(module);
				if ((defaultModule != null) && (defaultModule.startsWith("tr"))) {
					this.defaultModule = module;
				}
			}
			catch (Exception ex) {
 				log.error("Exception instantiating class: " + moduleClassName);
			}
		}
		catch (Exception ex) {
			log.error("Exception getting class for class name: " + moduleClassName);
		}
	}
	



	/**
	 * @return
	 */
	public String getFileName() {
		return fileName;
	}

	/**
	 * @param fileName
	 */
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	/**
	 * @return
	 */
	public String getIpAddress() {
		return ipAddress;
	}

	/**
	 * @param ipAddress
	 */
	public void setIpAddress(String ipAddress) {
		this.ipAddress = ipAddress;
	}

	/**
	 * @return
	 */
	public String getPort() {
		return port;
	}

	/**
	 * @param port
	 */
	public void setPort(String port) {
		this.port = port;
	}

	/**
	 * @return
	 */
	public String getApplicationName() {
		return applicationName;
	}

	/**
	 * @param applicationName
	 */
	public void setApplicationName(String applicationName) {
		this.applicationName = applicationName;
	}

	
	/**
	 * @return Returns the defaultModule.
	 */
	public Module getDefaultModule() {
		return defaultModule;
	}

	/**
	 * @return Returns the modules.
	 */
	public Vector getModules() {
		return modules;
	}

	/**
	 * @return Returns the location.
	 */
	public String getLocation() {
		return location;
	}

	/**
	 * @param location The location to set.
	 */
	public void setLocation(String location) {
		this.location = location;
	}

	/**
	 * @return Returns the application.
	 */
	public ClientApplication getApplication() {
		return application;
	}

	/**
	 * @return Returns the commandManager.
	 */
	public CommandDispatcher getCommandManager() {
		return commandManager;
	}

	/**
	 * @param commandManager The commandManager to set.
	 */
	public void setCommandManager(CommandDispatcher commandManager) {
		this.commandManager = commandManager;
	}

	/**
	 * @return Returns the connection.
	 */
	public ServiceConnection getConnection() {
		return connection;
	}

}
